﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509.Store;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Crypto.Digests;
using System.Security.Cryptography.Xml;
using System.Collections;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Asn1;


namespace Ekomunikacia
{
    public class Verify
    {
        public VerResultStructure VerifyDocument()
        {            
            string fileName = string.Empty;
            XmlDocument xmlDoc = new XmlDocument();
            VerResultStructure result = new VerResultStructure();

            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Xml file|*.xml";
            ofd.Title = "Otvorenie podpisaneho suboru";

            try
            {
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    fileName = ofd.FileName;
                    xmlDoc.PreserveWhitespace = true;
                    xmlDoc.Load(fileName);                    
                }
                else
                {
                    return result;
                }
            }
            catch (Exception ex)
            {
                result.Result = false;
                result.Message = "Nevalidna struktura XML dokumentu \r\n\r\n" + ex.ToString();
                return result;
            }

            //Start overovania
            result = VerEnvelope(xmlDoc);
            if (!result.Result)
                return result;
            result = VerSignature(xmlDoc);
            if (!result.Result)
                return result;


            return result;
        }

        /// <summary>
        /// Overenie datovej obalky: koreňový element musí obsahovať atribúty xmlns:xzep a xmlns:ds podľa profilu XADES_ZEP.
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <returns></returns>
        private VerResultStructure VerEnvelope(XmlDocument xmlDoc)
        {
            VerResultStructure verRes = new VerResultStructure();
            verRes.Result = true;

            if (!xmlDoc.DocumentElement.HasAttribute("xmlns:xzep") || (xmlDoc.DocumentElement.Attributes["xmlns:xzep"].Value != "http://www.ditec.sk/ep/signature_formats/xades_zep/v1.0"))
            {
                verRes.Result = false;
                verRes.Message = "Krok1: Obalka neobsahuje element xmlns:xzep alebo xmlns:xzep nema spravnu hodnotu!";
                return verRes;
            }

            if (!xmlDoc.DocumentElement.HasAttribute("xmlns:ds") || (xmlDoc.DocumentElement.Attributes["xmlns:ds"].Value != "http://www.w3.org/2000/09/xmldsig#"))
            {
                verRes.Result = false;
                verRes.Message = "Krok1: Obalka neobsahuje element xmlns:ds alebo xmlns:ds nema spravnu hodnotu!";
                return verRes;
            }

            return verRes;
        }

        /// <summary>
        /// Overenie XML Signature
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <returns></returns>
        private VerResultStructure VerSignature(XmlDocument xmlDoc)
        {
            VerResultStructure verRes = new VerResultStructure();
            verRes.Result = true;

            //######################## KROK 2 #####################################################
            //podporovane kryptograficke algoritmy ---------------
            List<string> podporKryptAlgs = new List<string>();
            podporKryptAlgs.Add("http://www.w3.org/2000/09/xmldsig#dsa-sha1");
            podporKryptAlgs.Add("http://www.w3.org/2000/09/xmldsig#rsa-sha1");
            podporKryptAlgs.Add("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
            podporKryptAlgs.Add("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
            //----------------------------------------------------
            
            //podporovane algoritmy na vypocet digitalneho otlacku ---------------
            List<string> podporDigAlgs = new List<string>();
            podporDigAlgs.Add("http://www.w3.org/2000/09/xmldsig#sha1");
            podporDigAlgs.Add("http://www.w3.org/2001/04/xmldsig-more#sha224");
            podporDigAlgs.Add("http://www.w3.org/2001/04/xmldsig-more#sha384");
            podporDigAlgs.Add("http://www.w3.org/2001/04/xmlenc#sha256");
            podporDigAlgs.Add("http://www.w3.org/2001/04/xmlenc#sha512");
            //--------------------------------------------------------------------
                        
            XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
            ns.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
            ns.AddNamespace("xades", "http://uri.etsi.org/01903/v1.3.2#");
            ns.AddNamespace("xzep", "http://www.ditec.sk/ep/signature_formats/xades_zep/v1.0");

            //kontrola podporovanych algoritmov
            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:SignedInfo/ds:SignatureMethod", ns) != null && !podporKryptAlgs.Contains(xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:SignedInfo/ds:SignatureMethod", ns).Attributes["Algorithm"].Value))
            {
                verRes.Result = false;
                verRes.Message = "Krok2: Nepodporovany algoritmus v SignatureMethod!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:SignedInfo/ds:CanonicalizationMethod", ns) != null && xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:SignedInfo/ds:CanonicalizationMethod", ns).Attributes["Algorithm"].Value != "http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
            {
                verRes.Result = false;
                verRes.Message = "Krok2: Nepodporovany algoritmus v CanonicalizationMethod!";
                return verRes;
            }
            //######################## KROK 2 #####################################################

            //######################## KROK 3 #####################################################
            //kontrola obsahu ds:Transforms a ds:DigestMethod vo všetkých referenciách v ds:SignedInfo – musia obsahovať URI niektorého z podporovaných algoritmov podľa profilu XAdES_ZEP

            XmlNodeList references = xmlDoc.DocumentElement.SelectNodes("//ds:Signature/ds:SignedInfo/ds:Reference", ns);
            foreach (XmlNode n in references)
            {
                if (n.SelectSingleNode("ds:Transforms/ds:Transform", ns) != null && n.SelectSingleNode("ds:Transforms/ds:Transform", ns).Attributes["Algorithm"].Value != "http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
                {
                    verRes.Result = false;
                    verRes.Message = "Krok3: Nepodporovany algoritmus v ds:Transforms/ds:Transform!";
                    return verRes;
                }

                if (!podporDigAlgs.Contains(n.SelectSingleNode("ds:DigestMethod", ns).Attributes["Algorithm"].Value))
                {
                    verRes.Result = false;
                    verRes.Message = "Krok3: Nepodporovany algoritmus v ds:DigestMethod!";
                    return verRes;
                }
            }
            //######################## end KROK 3 #################################################


            //######################## KROK 4 #####################################################
            //Core validation (podľa špecifikácie XML Signature)
            //dereferencovanie URI, kanonikalizácia referencovaných ds:Manifest elementov a overenie hodnôt odtlačkov ds:DigestValue
            
            XmlNodeList manifestes = xmlDoc.DocumentElement.SelectNodes("//ds:Manifest", ns);
            string manifestUri = string.Empty;
            
            //###vyberiem vsetky URI pre manifesty###
            foreach (XmlNode n in manifestes)
            {
                if (n.SelectSingleNode("ds:Reference", ns).Attributes["URI"] != null)
                    manifestUri = n.SelectSingleNode("ds:Reference", ns).Attributes["URI"].Value;
                else
                {
                    verRes.Result = false;
                    verRes.Message = "Krok4: Neexistuje atribut URI v ds:Manifest/ds:Reference!";
                    return verRes;
                }

                //najdem Object s URI a spracujem ho
                XmlNode myObject = xmlDoc.DocumentElement.SelectSingleNode("//ds:Object[@Id = '" + manifestUri.Substring(manifestUri.IndexOf("#") + 1) + "']", ns);
               
                if (myObject == null)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok4: Neexistuje referencia z URI na object s Id " + manifestUri.Substring(manifestUri.IndexOf("#") + 1) + "!";
                    return verRes;
                }
                               

                //vypocitanie digitalneho odtlacku pomocou algoritmu v ds:DigestMethod                
                //kanonikalizacia                
                string ObjectToCompute = CanonicalizeXml(myObject.OuterXml);
                        
                //ziskanie algoritmu
                string algoritmus = n.SelectSingleNode("ds:Reference/ds:DigestMethod", ns).Attributes["Algorithm"].Value.Substring(n.SelectSingleNode("ds:Reference/ds:DigestMethod", ns).Attributes["Algorithm"].Value.IndexOf("#") + 1);
                //ziskanie hodnoty
                UTF8Encoding encoding = new UTF8Encoding();
                byte[] digestValue = ComputeDigest(encoding.GetBytes(ObjectToCompute), algoritmus);
                string digestValueAsBase64 = Convert.ToBase64String(digestValue);

                //porovnanie
                string kontrola = n.SelectSingleNode("ds:Reference/ds:DigestValue", ns).InnerText;
                if (digestValueAsBase64 != kontrola)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok4: Vypocitana hodnota z deferencovaneho manifestu DigestValue nesedi s tym, co je ulozene v podpisanom dokumente v ds:DigestValue! \r\n\r\nVypocitana hodnota: '" + digestValueAsBase64 + "' \r\nUlozena hodnota: '" + kontrola + "'";
                    return verRes;
                }
                //#######
                                
            }

            //KROK4 2
            //kanonikalizácia ds:SignedInfo a overenie hodnoty ds:SignatureValue pomocou pripojeného podpisového certifikátu v ds:KeyInfo
            XmlNode si = xmlDoc.DocumentElement.SelectSingleNode("//ds:SignedInfo", ns);
            string signedInfo = CanonicalizeXml(si.OuterXml);
            string alg = si.SelectSingleNode("ds:SignatureMethod", ns).Attributes["Algorithm"].Value.Substring(si.SelectSingleNode("ds:SignatureMethod", ns).Attributes["Algorithm"].Value.IndexOf("#") + 1);
            


            
                

            //######################## end KROK 4 #################################################

            //######################## KROK 5 #####################################################
            #region overenie ostatných elementov profilu XAdES_ZEP, ktoré prináležia do špecifikácie XML Signature
            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature", ns) == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:Signature neexistuje, alebo nema specifikovany namespace xmlns:ds!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature", ns).Attributes["Id"] == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:Signature neobsahuje atribut Id!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature", ns).Attributes["Id"] == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:Signature neobsahuje atribut Id!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:SignatureValue", ns).Attributes["Id"] == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:SignatureValue neobsahuje atribut Id!";
                return verRes;
            }

#endregion

            #region overenie existencie referencií v ds:SignedInfo a hodnôt atribútov Id a Type voči profilu XAdES_ZEP pre

            List<string> refIds = new List<string>();
            List<string> refTypes = new List<string>();
            List<string> refURIs = new List<string>();
            foreach (XmlNode n in references)
            {
                if (n.Attributes["Id"] != null)
                {
                    refIds.Add(n.Attributes["Id"].Value);
                }
                else
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: Jedna z references neobsahuje atribut Id!";
                    return verRes;
                }

                if (n.Attributes["Type"] != null)
                {
                    string uri;
                    string xpath;
                    string type = n.Attributes["Type"].Value;

                    if (n.Attributes["URI"] != null)
                    {
                        uri = n.Attributes["URI"].Value.Substring(1);
                    }
                    else
                    {
                        verRes.Result = false;
                        verRes.Message = "Krok5: Jedna z references neobsahuje atribut URI!";
                        return verRes;
                    }

                    if (type == "http://uri.etsi.org/01903#SignedProperties")
                    {
                        xpath = String.Format("//*[@Id='{0}']", uri);
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns) == null)
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia; jedna z referencii neukazuje nikam!";
                            return verRes;
                        }
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns).Name != "xades:SignedProperties")
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia na xades:SignedProperties!";
                            return verRes;
                        }
                    }
                    else if (type == "http://www.w3.org/2000/09/xmldsig#SignatureProperties")
                    {
                        xpath = String.Format("//*[@Id='{0}']", uri);
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns) == null)
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia; jedna z referencii neukazuje nikam!";
                            return verRes;
                        }
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns).Name != "ds:SignatureProperties")
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia na ds:SignatureProperties!";
                            return verRes;
                        }
                    }
                    else if (type == "http://www.w3.org/2000/09/xmldsig#Object")
                    {
                        xpath = String.Format("//*[@Id='{0}']", uri);
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns) == null)
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia; jedna z referencii neukazuje nikam!";
                            return verRes;
                        }
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns).Name != "ds:KeyInfo")
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia na ds:KeyInfo!";
                            return verRes;
                        }
                    }
                    else if (type == "http://www.w3.org/2000/09/xmldsig#Manifest")
                    {
                        xpath = String.Format("//*[@Id='{0}']", uri);
                        if(xmlDoc.DocumentElement.SelectSingleNode(xpath, ns) == null)
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia; jedna z referencii neukazuje nikam!";
                            return verRes;
                        }
                        if (xmlDoc.DocumentElement.SelectSingleNode(xpath, ns).Name != "ds:Manifest")
                        {
                            verRes.Result = false;
                            verRes.Message = "Krok5: nespravna referencia; jedna z referencii neukazuje na manifest, ale niekde inde!";
                            return verRes;
                        }
                    }
                    else
                    {
                        verRes.Result = false;
                        verRes.Message = "Krok5: Jedna z references obsahuje nejaky divnu a neznamu ref v Type!";
                        return verRes;
                    }

                }
                else
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: Jedna z references neobsahuje atribut Type!";
                    return verRes;
                }

            }
#endregion

            #region overenie obsahu ds:KeyInfo

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:KeyInfo", ns).Attributes["Id"] == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: KeyInfo neobsahuje atribut Id!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:KeyInfo/ds:X509Data", ns) == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: KeyInfo neobsahuje element ds:X509Data!";
                return verRes;
            }
            else
            {
                if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate", ns) == null)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: ds:KeyInfo/ds:X509Data neobsahuje element ds:X509Certificate!";
                    return verRes;
                }

                if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509IssuerSerial", ns) == null)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: ds:KeyInfo/ds:X509Data neobsahuje element ds:X509IssuerSerial!";
                    return verRes;
                }

                if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509SubjectName", ns) == null)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: ds:KeyInfo/ds:X509Data neobsahuje element ds:X509SubjectName!";
                    return verRes;
                }
            }

            ///TODO: hodnoty elementov ds:X509IssuerSerial a ds:X509SubjectName súhlasia s príslušnými hodnatami v certifikáte, 
            ///ktorý sa nachádza v ds:X509Certificate

            #endregion

            #region overenie obsahu ds:SignatureProperties

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:Object/ds:SignatureProperties", ns).Attributes["Id"] == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:SignatureProperties neobsahuje atribut Id!";
                return verRes;
            }

            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:Object/ds:SignatureProperties/ds:SignatureProperty/xzep:SignatureVersion", ns) == null || xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature/ds:Object/ds:SignatureProperties/ds:SignatureProperty/xzep:ProductInfos", ns) == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok5: ds:SignatureProperties neobsahuje element ds:SignatureProperty/xzep:SignatureVersion alebo ds:SignatureProperty/xzep:ProductInfos!";
                return verRes;
            }

            XmlNodeList nl = xmlDoc.DocumentElement.SelectNodes("//ds:Signature/ds:Object/ds:SignatureProperties/ds:SignatureProperty", ns);
            foreach (XmlNode n in nl)
            {
                if (n.Attributes["Target"].Value.Substring(1) != xmlDoc.DocumentElement.SelectSingleNode("//ds:Signature", ns).Attributes["Id"].Value)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: jeden z ds:SignatureProperty a jej atribut Target neukazuje na ds:Signature Id!";
                    return verRes;
                }
            }

            #endregion

            #region overenie ds:Manifest elementov

            nl = xmlDoc.DocumentElement.SelectNodes("//ds:Signature/ds:Object/ds:Manifest", ns);
            foreach (XmlNode n in nl)
            {
                if (n.Attributes["Id"] == null)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: jeden z manifestov neobsahuje atribut Id!";
                    return verRes;
                }

                if (n.SelectSingleNode("ds:Reference/ds:Transforms/ds:Transform", ns).Attributes["Algorithm"].Value != "http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: Nepodporovany algoritmus v ds:Transforms/ds:Transform!";
                    return verRes;
                }

                if (!podporDigAlgs.Contains(n.SelectSingleNode("ds:Reference/ds:DigestMethod", ns).Attributes["Algorithm"].Value))
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: Nepodporovany algoritmus v ds:DigestMethod!";
                    return verRes;
                }

                if (n.SelectSingleNode("ds:Reference", ns).Attributes["Type"] != null && n.SelectSingleNode("ds:Reference", ns).Attributes["Type"].Value != "http://www.w3.org/2000/09/xmldsig#Object")
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: Nespravna hodnota v atribute Type v ds:Reference v manifeste!";
                    return verRes;
                }

                XmlNodeList refers = n.SelectNodes("ds:Reference", ns);
                if (refers.Count != 1)
                {
                    verRes.Result = false;
                    verRes.Message = "Krok5: ds:Manifest neobsahuje PRAVE JEDNU referenciu na ds:Object!";
                    return verRes;
                }
            }


            #endregion

            #region overenie referencií v elementoch ds:Manifest

            //to nie je to iste ako v kroku 4?

            #endregion
            //######################## KROK 5 #####################################################

            //######################## KROK 6 #####################################################
            #region Overenie časovej pečiatky
            
            //------------LOAD CERTIFIKATU
            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:KeyInfo/ds:X509Data/ds:X509Certificate", ns) == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok4: Bizardna vec, toto XML nema cestu //ds:KeyInfo/ds:X509Data/ds:X509Certificate , co nesedi s definiciou!";
                return verRes;
            }
            string certString = xmlDoc.DocumentElement.SelectSingleNode("//ds:KeyInfo/ds:X509Data/ds:X509Certificate", ns).InnerText;
            X509Certificate cert = null;
            byte[] cert1 = Base64.Decode(certString);
            cert = new X509CertificateParser().ReadCertificate(cert1);
                        
            if (xmlDoc.DocumentElement.SelectSingleNode("//ds:Object/xades:QualifyingProperties/xades:UnsignedProperties/xades:UnsignedSignatureProperties/xades:SignatureTimeStamp/xades:EncapsulatedTimeStamp", ns) == null)
            {
                verRes.Result = false;
                verRes.Message = "Krok6: Podpisane xml neobsahuje casovu peciatku, alebo predpisana cesta k casovej peciatke neexistuje!";
                return verRes;
            }

            //!!!!!!!!!overenie platnosti podpisovaho certikatu casovej peciatky!!!!!!!!
            string sTimestampToken = xmlDoc.DocumentElement.SelectSingleNode("//ds:Object/xades:QualifyingProperties/xades:UnsignedProperties/xades:UnsignedSignatureProperties/xades:SignatureTimeStamp/xades:EncapsulatedTimeStamp", ns).InnerText;
            byte[] bTimestampToken = Base64.Decode(sTimestampToken);
            TimeStampToken token = new TimeStampToken(new CmsSignedData(bTimestampToken));
            IX509Store s = token.GetCertificates("Collection");
            
            X509Certificate tsCertificate = (X509Certificate) new ArrayList(s.GetMatches(token.SignerID))[0];

            if (!tsCertificate.IsValidNow)
            {
                verRes.Result = false;
                verRes.Message = "Krok6: Certifikat casovej peciatky je neplatny!";
                return verRes;
            }
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //!!!!!!!!!!!!overenie voci CRL!!!!!!!!!!!!!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            FileStream crlStream = new FileStream("DTCCACrl.crl", FileMode.Open);
            X509Crl crl = new X509CrlParser().ReadCrl(crlStream);
            crlStream.Close();

            if (crl.IsRevoked(tsCertificate))
            {
                verRes.Result = false;
                verRes.Message = "Krok6: Certifikat casovej peciatky sa nachadza na CRL liste!";
                return verRes;
            }

            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

            //TODO: overenie MessageImprint z časovej pečiatky voči podpisu ds:SignatureValue

            #endregion
            //######################## KROK 6 #####################################################

            //######################## KROK 7 #####################################################
            #region Overenie platnosti podpisového certifikátu

            //este overim, ci na CRL sa nenachadza nahodou certifikat, ktorym je podpisane XML
            if (crl.IsRevoked(cert))
            {
                verRes.Result = false;
                verRes.Message = "Krok7: Certifikat, ktorym je podpisane XML je na CRL liste!";
                return verRes;
            }

            DateTime casOpeciatkovania = token.TimeStampInfo.GenTime;

            if (cert.NotBefore > casOpeciatkovania && cert.NotAfter < casOpeciatkovania)
            {
                verRes.Result = false;
                verRes.Message = "Krok7: Certifikat, ktorym je podpisane XML nie je validny voci casovej peciatke! \r\n\r\nDokument bol opeciatkovany: " + casOpeciatkovania.ToString() + "  \r\n\r\nCertikat bol vsak platny \r\nod: " + cert.NotBefore.ToString() + " \r\ndo: " + cert.NotAfter.ToString();
                return verRes;
            }
            
            #endregion
            //######################## KROK 7 #####################################################

            return verRes;
        }

        //priklad pocitania digitalneho otlacku, (thx to p.Major)
        private static byte[] ComputeDigest(byte[] source, string digestAlg)
        {
            if (source == null)
            {
                return null;
            }
            
            IDigest digestProv = null;

            switch (digestAlg)
            {
                case "sha1":
                    digestProv = new Sha1Digest();
                    break;
                case "sha256":
                    digestProv = new Sha256Digest();
                    break;
                case "sha384":
                    digestProv = new Sha384Digest();
                    break;
                case "sha512":
                    digestProv = new Sha512Digest();
                    break;
            }
            
            digestProv.BlockUpdate(source, 0, source.Length);
            byte[] digestValue = new byte[digestProv.GetDigestSize()];
            digestProv.DoFinal(digestValue, 0);

            return digestValue;
        }

        private string CanonicalizeXml(string xml)
        {
            XmlDocument x = new XmlDocument();
            x.PreserveWhitespace = true;
            x.LoadXml(xml);

            XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
            transform.LoadInput(x);
            Stream output = (Stream)transform.GetOutput();
            StreamReader reader = new StreamReader(output);
            return reader.ReadToEnd();
        }

        private string GetSignatureValue(XmlDocument xmlDoc)
        {
            //XmlDocument xmlDoc = new XmlDocument();
            //xmlDoc.Load(fileName);
            string result = string.Empty;

            try
            {
                result = xmlDoc.GetElementsByTagName("ds:SignatureValue")[0].InnerText; //tento retazec bude v base64 formate
            }
            catch
            {
                throw new Exception("Nepodarilo sa ziskat SignatureValue z podpisaneho dokumentu");
            }

            return result;
        }
    }
}
